(function($) {

    /**
     * Initialize editor instances.
     *
     * @todo Is the following note still valid for 3.x?
     * This function needs to be called before the page is fully loaded, as
     * calling tinyMCE.init() after the page is loaded breaks IE6.
     *
     * @param editorSettings
     *   An object containing editor settings for each input format.
     */
    Drupal.wysiwyg.editor.init.tinymce = function(settings) {
        // @see #454992: drupal_get_js() must not use 'q' as query string.
        if (tinymce.query == 'q') {
            tinymce.query = '';
        }
        // If JS compression is enabled, TinyMCE is unable to autodetect its global
        // settinge, hence we need to define them manually.
        // @todo Move global library settings somewhere else.
        tinyMCE.baseURL = settings.global.editorBasePath;
        tinyMCE.srcMode = (settings.global.execMode == 'src' ? '_src' : '');
        tinyMCE.gzipMode = (settings.global.execMode == 'gzip');

        // Initialize editor configurations.
        for (var format in settings) {
            if (format == 'global') {
                continue;
            }
            ;
            tinyMCE.init(settings[format]);
            if (Drupal.settings.wysiwyg.plugins[format]) {
                // Load native external plugins.
                // Array syntax required; 'native' is a predefined token in JavaScript.
                for (var plugin in Drupal.settings.wysiwyg.plugins[format]['native']) {
                    tinymce.PluginManager.load(plugin, Drupal.settings.wysiwyg.plugins[format]['native'][plugin]);
                }
                // Load Drupal plugins.
                for (var plugin in Drupal.settings.wysiwyg.plugins[format].drupal) {
                    Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins[format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
                }
            }
        }
    };

    /**
     * Attach this editor to a target element.
     *
     * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook.
     */
    Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
        // Configure editor settings for this input format.
        var ed = new tinymce.Editor(params.field, settings);
        // Reset active instance id on any event.
        ed.onEvent.add(function(ed, e) {
            Drupal.wysiwyg.activeId = ed.id;
        });
        // Make toolbar buttons wrappable (required for IE).
        ed.onPostRender.add(function(ed) {
            var $toolbar = $('<div class="wysiwygToolbar"></div>');
            $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function() {
                $('<div></div>').addClass(this.className).append($(this).children()).appendTo($toolbar);
            });
            $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar);
            $('#' + ed.editorContainer + ' table.mceToolbar').remove();
        });

        // Remove TinyMCE's internal mceItem class, which was incorrectly added to
        // submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
        // for placeholder elements. If preemptively set, the class prevents (native)
        // editor plugins from gaining an active state, so we have to manually remove
        // it prior to attaching the editor. This is done on the client-side instead
        // of the server-side, as Wysiwyg has no way to figure out where content is
        // stored, and the class only affects editing.
        $field = $('#' + params.field);
        $field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));

        // Attach editor.
        ed.render();
    };

    /**
     * Detach a single or all editors.
     *
     * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
     */
    Drupal.wysiwyg.editor.detach.tinymce = function(context, params) {
        if (typeof params != 'undefined') {
            var instance = tinyMCE.get(params.field);
            if (instance) {
                instance.save();
                instance.remove();
            }
        }
        else {
            // Save contents of all editors back into textareas.
            tinyMCE.triggerSave();
            // Remove all editor instances.
            for (var instance in tinyMCE.editors) {
                tinyMCE.editors[instance].remove();
            }
        }
    };

    Drupal.wysiwyg.editor.instance.tinymce = {
        addPlugin: function(plugin, settings, pluginSettings) {
            if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
                return;
            }
            tinymce.create('tinymce.plugins.' + plugin, {
                /**
                 * Initialize the plugin, executed after the plugin has been created.
                 *
                 * @param ed
                 *   The tinymce.Editor instance the plugin is initialized in.
                 * @param url
                 *   The absolute URL of the plugin location.
                 */
                init: function(ed, url) {
                    // Register an editor command for this plugin, invoked by the plugin's button.
                    ed.addCommand(plugin, function() {
                        if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
                            var data = {format: 'html', node: ed.selection.getNode(), content: ed.selection.getContent()};
                            // TinyMCE creates a completely new instance for fullscreen mode.
                            var instanceId = ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id;
                            Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instanceId);
                        }
                    });

                    // Register the plugin button.
                    ed.addButton(plugin, {
                        title: settings.iconTitle,
                        cmd: plugin,
                        image: settings.icon
                    });

                    // Load custom CSS for editor contents on startup.
                    ed.onInit.add(function() {
                        if (settings.css) {
                            ed.dom.loadCSS(settings.css);
                        }
                    });

                    // Attach: Replace plain text with HTML representations.
                    ed.onBeforeSetContent.add(function(ed, data) {
                        if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
                            data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, ed.id);
                            data.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(data.content);
                        }
                    });

                    // Detach: Replace HTML representations with plain text.
                    ed.onGetContent.add(function(ed, data) {
                        if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
                            data.content = Drupal.wysiwyg.plugins[plugin].detach(data.content, pluginSettings, ed.id);
                        }
                    });

                    // isNode: Return whether the plugin button should be enabled for the
                    // current selection.
                    ed.onNodeChange.add(function(ed, command, node) {
                        if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
                            command.setActive(plugin, Drupal.wysiwyg.plugins[plugin].isNode(node));
                        }
                    });
                },
                /**
                 * Return information about the plugin as a name/value array.
                 */
                getInfo: function() {
                    return {
                        longname: settings.title
                    };
                }
            });

            // Register plugin.
            tinymce.PluginManager.add(plugin, tinymce.plugins[plugin]);
        },
        openDialog: function(dialog, params) {
            var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field;
            var editor = tinyMCE.get(instanceId);
            editor.windowManager.open({
                file: dialog.url + '/' + instanceId,
                width: dialog.width,
                height: dialog.height,
                inline: 1
            }, params);
        },
        closeDialog: function(dialog) {
            var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field;
            var editor = tinyMCE.get(instanceId);
            editor.windowManager.close(dialog);
        },
        prepareContent: function(content) {
            // Certain content elements need to have additional DOM properties applied
            // to prevent this editor from highlighting an internal button in addition
            // to the button of a Drupal plugin.
            var specialProperties = {
                img: {'class': 'mceItem'}
            };
            var $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
            // Find all placeholder/replacement content of Drupal plugins.
            $content.find('.drupal-content').each(function() {
                // Recursively process DOM elements below this element to apply special
                // properties.
                var $drupalContent = $(this);
                $.each(specialProperties, function(element, properties) {
                    $drupalContent.find(element).andSelf().each(function() {
                        for (var property in properties) {
                            if (property == 'class') {
                                $(this).addClass(properties[property]);
                            }
                            else {
                                $(this).attr(property, properties[property]);
                            }
                        }
                    });
                });
            });
            return $content.html();
        },
        insert: function(content) {
            content = this.prepareContent(content);
            var instanceId = this.isFullscreen() ? 'mce_fullscreen' : this.field;
            tinyMCE.execInstanceCommand(instanceId, 'mceInsertContent', false, content);
        },
        isFullscreen: function() {
            // TinyMCE creates a completely new instance for fullscreen mode.
            return tinyMCE.activeEditor.id == 'mce_fullscreen' && tinyMCE.activeEditor.getParam('fullscreen_editor_id') == this.field;
        }
    };

})(jQuery);
